package ezy.widget.pullrefresh;


import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.Adapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Scroller;

public class PullRefreshList extends ListView {

    public enum State {
        NONE,
        NORMAL,
        PULLING,
        READY,
        LOADING
    }

    public interface OnRefreshListener {
        void onRefresh();
        void onLoadMore(int index);
    }


	private Scroller _scroll;
	private OnRefreshListener _refreshListener;

	private PullLayout _header;
    private PullLayout _footer;


    private State _state = State.NONE;
    private int _page = 1;
    private boolean _isHeader = true;


	public PullRefreshList(Context context) {
		super(context);
		initWithContext(context);
	}
    public PullRefreshList(Context context, AttributeSet attrs) {
		super(context, attrs);
		initWithContext(context);
	}
    public PullRefreshList(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initWithContext(context);
    }

	private void initWithContext(Context context) {
		_scroll = new Scroller(context, new DecelerateInterpolator());

        _header = new PullLayout(getContext(), true);
        _footer = new PullLayout(getContext(), false);
        _header.setEnabled(false);
        _footer.setEnabled(false);
        _header.setVisibility(View.GONE);
        addHeaderView(_header);

 	}

    private boolean _isRefreshEnabled = true;
    private boolean _isLoadMoreEnabled = false;
    private boolean _isAutoLoad = false;
    private boolean _hasMoreData = false;

    public int getPageIndex() {
        return _page;
    }
    public boolean isRefreshEnabled(){
        return _isRefreshEnabled;
    }
    public boolean isLoadMoreEnabled() {
        return _isLoadMoreEnabled;
    }
    public boolean isAutoLoad() {
        return _isAutoLoad;
    }
    private boolean hasMoreData() {
        return _hasMoreData;
    }


    public void setIsRefreshEnabled(boolean value) {
        _isRefreshEnabled = value;
    }
    public void setIsLoadMoreEnabled(boolean value) {
        _isLoadMoreEnabled = value;
    }

    /** 滑动到底部是否自动加载更多数据 */
    public void setIsAutoLoad(boolean value) {
        _isAutoLoad = value;
    }
    /** 是否有更多数据 */
    public void setHasMoreData(boolean value) {
        _hasMoreData = value;
        if (_footer.isEnabled()) {
            _footer.onState(value ? State.NORMAL : State.NONE);
        }
    }

    public void setOnRefreshListener(OnRefreshListener value) {
        _refreshListener = value;
    }
    public void setOnScrollListener(OnScrollListener value) {
        _scrollListener = value;
    }

    public void setHeader(IPullIndicator value) {
        if (!isInited) {
            _header.indicator = value;
        }
    }
    public void setFooter(IPullIndicator value) {
        if (!isInited) {
            _footer.indicator = value;
        }
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        init();
        super.setAdapter(adapter);
    }

    private boolean isInited = false;
    private void init() {
        if (isInited) {
            return;
        }
        if (!isRefreshEnabled() && !isLoadMoreEnabled()) {
            return;
        }
        _header.setEnabled(isRefreshEnabled());
        _footer.setEnabled(isLoadMoreEnabled());

        super.setOnScrollListener(_scrollProxy);
        super.setOnTouchListener(_touchListener);

        if (isRefreshEnabled()) {
            _header.setVisibility(VISIBLE);
            if (_header.indicator == null) {
                _header.indicator = new PullHeader();
            }
            _header.init();
            _header.onState(State.NORMAL);

        }

        if (isLoadMoreEnabled()) {
            addFooterView(_footer);
            if (_footer.indicator == null) {
                _footer.indicator = new PullFooter();
            }
            _footer.init();
            _footer.onState(State.NORMAL);

            _footer.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    loadMore();
                }
            });
        }
        isInited = true;
    }

    public void refresh() {
        if (isRefreshEnabled() && _state != State.LOADING) {
            _isHeader = true;
            onState(State.LOADING);
        }
    }
    public void loadMore() {
        if (isLoadMoreEnabled() && _state != State.LOADING && hasMoreData()) {
            _isHeader = false;
            onState(State.LOADING);
        }
    }
    public void finish() {
        if (_state == State.LOADING) {
            onState(State.NORMAL);
        }
    }


    private void onState(State state) {
        if (_state == state) {
            return;
        }
        _state = state;

        PullLayout layout = _isHeader ? _header : _footer;
        if (_isHeader || hasMoreData()) {
            layout.onState(state);
        }
        switch (state) {
        case NORMAL:
            onReset(layout);
            break;
        case PULLING:
            break;
        case READY:
            break;
        case LOADING:
            onReset(layout);
            if (_refreshListener == null) {
                return;
            }
            if (_isHeader) {
                _page = 1;
                _refreshListener.onRefresh();
            } else {
                _page++;
                _refreshListener.onLoadMore(_page);
            }
            break;
        }
    }


    private void onReset(PullLayout layout) {
        int startY = layout.getLayoutHeight();
        if (startY <= 0) {
            return;
        }
        _scroll.startScroll(0, startY, 0, -layout.getPullValue(), SCROLL_DURATION);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (_scroll.computeScrollOffset()) {
            PullLayout layout = _isHeader ? _header : _footer;
            layout.setLayoutHeight(_scroll.getCurrY());
            postInvalidate();
        }
        super.computeScroll();
    }




    private final static int SCROLL_DURATION = 400;

    private AbsListView.OnScrollListener _scrollListener;

    private OnTouchListener _touchListener = new OnTouchListener() {

        private final static float OFFSET_RADIO = 1.8f;

        float startY = -1;
        boolean isPulling = false;


        public boolean isReadyForPullUp() {
            return !ViewCompat.canScrollVertically(PullRefreshList.this, 1);
        }
        public boolean isReadyForPullDown() {
            return !ViewCompat.canScrollVertically(PullRefreshList.this, -1);
        }
        private void restart(MotionEvent event) {
            Log.w("ezy", "restart ");
            MotionEvent cancel = MotionEvent.obtain(event);
            cancel.setAction(MotionEvent.ACTION_CANCEL);
            dispatchTouchEvent(cancel);
            MotionEvent down = MotionEvent.obtain(event);
            down.setAction(MotionEvent.ACTION_DOWN);
            dispatchTouchEvent(down);
        }
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            if (startY == -1 || event.getAction() == MotionEvent.ACTION_DOWN) {
                if (isReadyForPullDown() || isReadyForPullUp()) {
                    startY = event.getRawY();
                }
            }
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isPulling = false;
                break;
            case MotionEvent.ACTION_MOVE:
                final float deltaY = event.getRawY() - startY;
                if (isReadyForPullDown()) {
                    if (_header.isEnabled() && deltaY > 0) {
                        isPulling = true;
                        onPulling(true, deltaY / OFFSET_RADIO);
                        return true;
                    } else if (isPulling) {
                        restart(event);
                        return true;
                    }
//                    Log.w("ezy", "isPulling = false, isHeader = true, delta = " + deltaY + ", state = " + _state);
                } else if (isReadyForPullUp()) {
                    if (_footer.isEnabled() && -deltaY > 0){
                        isPulling = true;
                        onPulling(false, -deltaY / OFFSET_RADIO);
                        return true;
                    } else if (isPulling) {
                        restart(event);
                        return true;
                    }
//                    Log.w("ezy", "isPulling = false, isHeader = false, delta = " + deltaY + ", state = " + _state);
                } else {
//                    Log.w("ezy", "noPulling ");
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
            default:
                startY = -1;
                if (isPulling) {
                    isPulling = false;
                    onPulled();
                    return true;
                }
                break;
            }
            return false;
        }
    };

    private void onPulling(boolean isHeader, float delta) {
//        Log.w("ezy", "isPulling = true, isHeader = " + isHeader + ", delta = " + delta + ", state = " + _state);
        _isHeader = isHeader;
        PullLayout layout = isHeader ? _header : _footer;
        layout.setPullValue((int) delta);
        if (layout.isEnabled() && _state != State.LOADING) {
            onState(layout.isReady() ? State.READY : State.PULLING);
        }
    }
    private void onPulled() {
        PullLayout layout = _isHeader ? _header : _footer;
//        Log.w("ezy", "onPulled, isHeader = " + _isHeader + ", state = " + _state);
        if (_state == State.READY) {
            if (!layout.isHeader && !hasMoreData())  {
                onState(State.NORMAL);
            } else {
                onState(State.LOADING);
            }
        } else if (_state == State.LOADING) {
            onReset(layout);
        } else {
            onState(State.NORMAL);
        }
    }


    private ListWrap _scrollProxy = new ListWrap();
    class ListWrap implements OnScrollListener {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (isAutoLoad()) {
                switch (scrollState) {
                case OnScrollListener.SCROLL_STATE_IDLE:
                case OnScrollListener.SCROLL_STATE_FLING:
                    final Adapter adapter = getAdapter();
                    if (adapter != null && !adapter.isEmpty() && getLastVisiblePosition() >= adapter.getCount() - 2) {
                        loadMore();
                    }
                    break;
                }
            }
            if (_scrollListener != null) {
                _scrollListener.onScrollStateChanged(view, scrollState);
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (_scrollListener != null) {
                _scrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
            }
        }
    };



}
